#include <shaders.h>

#include <memory>
#include <iostream>

void 
shaders::printShaderInfoLog(GLuint obj) {
  int infologLength = 0;
  int charsWritten  = 0;
  char *infoLog;

  glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength);

  if (infologLength > 0) {
    infoLog = (char *)malloc(infologLength);
    glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);
    printf("%s\n",infoLog);
    free(infoLog);
  }
}

void 
shaders::printProgramInfoLog(GLuint obj) {
  int infologLength = 0;
  int charsWritten  = 0;
  char *infoLog;

  glGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength);

  if (infologLength > 0) {
    infoLog = (char *)malloc(infologLength);
    glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog);
    printf("%s\n",infoLog);
    free(infoLog);
  }
}

char *
shaders::textFileRead(char *fn) {

  FILE *fp;
  char *content = NULL;
  int count;

  fp = fopen(fn, "rb");

  fseek(fp, 0, SEEK_END);
  count = ftell(fp);

  fclose(fp);

  if (fn != NULL) {
    fp = fopen(fn, "rt");
    if (fp != NULL) {
      if (count > 0) {
        content = (char *)malloc(sizeof(char) * (count+1));
        count = (size_t) fread(content,sizeof(char), (size_t) count,fp);
        content[count] = '\0';
      }
      fclose(fp);
    }
  }
  return content;
} 

void 
shaders::setShaders() {

  char *vs,*fs;

  vertex_shader = glCreateShader(GL_VERTEX_SHADER);
  fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);	

  vs = textFileRead("vertex.vert");
  fs = textFileRead("fragment.frag");

  const char * vv = vs;
  const char * ff = fs;

  glShaderSource(vertex_shader, 1, &vv, NULL);
  glShaderSource(fragment_shader, 1, &ff, NULL);

  free(vs);
  free(fs);

  program = glCreateProgram();
  glAttachShader(program, fragment_shader);

  glCompileShader(fragment_shader);
  printShaderInfoLog(fragment_shader);

  glCompileShader(vertex_shader);
  printShaderInfoLog(vertex_shader);

  glAttachShader(program, vertex_shader);

  glLinkProgram(program);
  printProgramInfoLog(program);

  glUseProgram(program);
}

bool
shaders::initialize() {
  // initialize shaders
  glewInit();

  if (GLEW_ARB_vertex_shader && GLEW_ARB_fragment_shader) {
  std::cout << "Ready for GLSL" << std::endl;
  }
  else {
  std::cout << "Not totally ready :(" << std::endl;
  return false;
  }

  if (glewIsSupported("GL_VERSION_2_0")) {
  std::cout << "Ready for OpenGL 2.0" << std::endl;
  }
  else {
  std::cout << "OpenGL 2.0 not supported" << std::endl;
  return false;
  }

  setShaders();
}

shaders::shaders() {
}

shaders::~shaders() {
  glDetachShader(program, vertex_shader);
  glDetachShader(program, fragment_shader);

  glDeleteShader(vertex_shader); 
  glDeleteShader(fragment_shader); 
  glDeleteProgram(program);
}